#include "MaterialBRDFNodeModel.h"
#include "Object/NewObject.h"

#include "MaterialGraph/Pin/MaterialGraphPinModel.h"

void RMaterialBRDFNodeModel::FirstTimeInitializeObject()
{
    //create output pin
    // auto OutputPin = NewObject<RMaterialGraphPinModel>(this);
    // OutputPin->bIsOutput = true;
    // OutputPin->ValueType = EMaterialGraphValueType::Vector4D;
    // Outputs.Add(OutputPin);

    //create input pins
    auto BaseColorPin = NewObject<RMaterialGraphPinModel>(this);
    BaseColorPin->bIsOutput = false;
    BaseColorPin->ValueType = EMaterialGraphValueType::Vector3D;
    BaseColorPin->DefaultInputX= 1.0f;
    BaseColorPin->DefaultInputY= 1.0f;
    BaseColorPin->DefaultInputZ= 1.0f;
    //although it is not used, set W to 1.0f to make sure
    //color in widget is not transparent
    BaseColorPin->DefaultInputW= 1.0f;
    Inputs.Add(BaseColorPin);

    auto MetallicPin = NewObject<RMaterialGraphPinModel>(this);
    MetallicPin->bIsOutput = false;
    MetallicPin->ValueType = EMaterialGraphValueType::Vector1D;
    MetallicPin->DefaultInputX= 0.5f;
    Inputs.Add(MetallicPin);

    auto RoughnessPin = NewObject<RMaterialGraphPinModel>(this);
    RoughnessPin->bIsOutput = false;
    RoughnessPin->ValueType = EMaterialGraphValueType::Vector1D;
    RoughnessPin->DefaultInputX = 0.5f;
    Inputs.Add(RoughnessPin);

    auto NormalPin = NewObject<RMaterialGraphPinModel>(this);
    NormalPin->bIsOutput = false;
    NormalPin->ValueType = EMaterialGraphValueType::Vector3D;
    NormalPin->DefaultInputX= 0.0f;
    NormalPin->DefaultInputY= 0.0f;
    NormalPin->DefaultInputZ= 1.0f;
    
    Inputs.Add(NormalPin);

}

CString RMaterialBRDFNodeModel::GenerateExpression()
{
    CString Result;

    CString Albedo =
        U"vec3 Albedo = "+Inputs[0]->GetValueExpression()+U";\n";
    
    CString Metallic =
        U"float Metallic = "+Inputs[1]->GetValueExpression()+U";\n";

    CString Roughness =
        U"float Roughness = "+Inputs[2]->GetValueExpression()+U";\n";

    CString Normal =
        U"vec3 Normal = "+Inputs[3]->GetValueExpression()+U";\n";

    return Albedo+Metallic+Roughness+Normal;

}

CString RMaterialBRDFNodeModel::GetShaderTemplate() const
{
    return 
U"\
#version 460\n\
#extension GL_ARB_separate_shader_objects : enable\n\
#extension GL_ARB_shading_language_420pack : enable\n\
\
layout (set=2, binding = 0) uniform SceneBufferObject \n\
{ \n\
    mat4 ProjectionView; \n\
    float CameraNear; \n\
    float CameraFar; \n\
\
    mat4 ProjectMatrix; \n\
    mat4 ViewMatrix; \n\
    vec3 CameraPosition; \n\
    vec3 CameraForwardDirection; \n\
    vec3 CameraRightDirection; \n\
    vec3 CameraUpDirection; \n\
} SceneBuffer; \n\
\
layout (location = 0) in vec3 inNormal;\n\
layout (location = 1) in vec2 inUV;\n\
layout (location = 2) in vec3 inWorldPos;\n\
layout (location = 3) in float inCameraNearPlane; \n\
layout (location = 4) in float inCameraFarPlane; \n\
\
layout (location = 0) out vec4 outAlbedo; \n\
layout (location = 1) out vec4 outPosition; \n\
layout (location = 2) out vec4 outNormal; \n\
layout (location = 3) out vec4 outMisc; \n\
\
/*HEADERS_AREA*/                 \n\
float linearDepth(float depth)\n\
{\n\
	float z = depth * 2.0f - 1.0f; \n\
    return (2.0f * inCameraNearPlane * inCameraFarPlane)/(inCameraFarPlane + inCameraNearPlane - z * (inCameraFarPlane - inCameraNearPlane));  \n\
}\n\
\n\
vec3 GetNormalFromMap(vec3 InNormalTextureValue)\n\
{\n\
    vec3 tangentNormal=InNormalTextureValue*2.0-1.0;\n\
    vec3 Q1  = dFdx(inWorldPos);\n\
    vec3 Q2  = dFdy(inWorldPos);\n\
    vec2 st1 = dFdx(inUV);\n\
    vec2 st2 = dFdy(inUV);\n\
\n\
    vec3 N   = normalize(inNormal);\n\
    vec3 T  = normalize(Q1*st2.t - Q2*st1.t);\n\
    vec3 B  = -normalize(cross(N, T));\n\
    mat3 TBN = mat3(T, B, N);\n\
\n\
    return normalize(TBN * InNormalTextureValue);\n\
}\n\
\n\
void main()\n\
{\n\
    float Ao=1.f;\n\
	float Depth = linearDepth(gl_FragCoord.z);\n\
/*CALCULATE_AREA*/\n\
    outMisc.r = Depth;\n\
	outNormal = vec4(GetNormalFromMap(Normal), Roughness);\n\
	outPosition = vec4(inWorldPos, Metallic);\n\
	outAlbedo=vec4(Albedo,Ao); \n\
}\
"
;
}
